var convert = require('xml-js')
var axios = require('axios')
const https = require('https')

class ServiceXmlParse {
  constructor() {}
  WMTSXML = 'http://www.opengis.net/wmts/1.0'
  WMSXML = 'http://www.opengis.net/wms'
  SUPERMAPVT = 'http://www.supermap.com/SuperMapCache/vectorltile'

  parseXml2js(xml) {
    const option = {
      ignoreDeclaration: true,
      compact: true,
      trim: true,
      ignoreInstruction: true,
      ignoreComment: true,
      ignoreCdata: true,
      ignoreDoctype: true
    }
    function RemoveJsonTextAttribute(value, parentElement) {
      try {
        var keyNo = Object.keys(parentElement._parent).length
        var keyName = Object.keys(parentElement._parent)[keyNo - 1]
        parentElement._parent[keyName] = value
      } catch (e) {}
    }
    return convert.xml2js(xml, { ...option, textFn: RemoveJsonTextAttribute })
  }

  getWMTSParams(obj) {
    const wmstList = []
    if (obj.Capabilities) {
      const { _attributes, Contents } = obj.Capabilities
      if (_attributes?.xmlns !== this.WMTSXML) {
        return '不是标准WMTS元文件地址'
      }
      const { Layer, TileMatrixSet } = Contents
      if (!Layer || !TileMatrixSet) {
        return '图层与TileMatrixSet为空，无法解析'
      }
      const info = {
        url: null,
        layer: null,
        style: null,
        tileMatrixSetID: null,
        format: null,
        tileMatrixLabels: null,
        crs: null,
        center: null
      }

      const tileSet = Array.isArray(TileMatrixSet)
        ? TileMatrixSet[0]
        : TileMatrixSet
      info.tileMatrixSetID = tileSet['ows:Identifier']
      info.crs = tileSet['ows:SupportedCRS']
      info.tileMatrixLabels = tileSet.TileMatrix.map((s) => s['ows:Identifier'])

      let LayerInfo = Layer

      if (!Array.isArray(LayerInfo)) {
        LayerInfo = [LayerInfo]
      }

      LayerInfo.forEach((layer) => {
        let resourceURL = layer?.ResourceURL
        if (!Array.isArray(resourceURL)) {
          resourceURL = [resourceURL]
        }
        info.format = ''
        const resourceURLItem = resourceURL.filter(
          (s) => s?._attributes?.resourceType === 'tile'
        )
        const pngResource =
          resourceURLItem.find((s) =>
            s?._attributes?.format?.endsWith('png')
          ) || resourceURLItem[0]
        if (pngResource) {
          info.url = pngResource?._attributes?.template
          info.format = pngResource?._attributes?.format
        }
        info.layer = layer['ows:Identifier']
        info.style = layer?.Style?.['ows:Identifier']

        const wgsBox = layer['ows:WGS84BoundingBox']
        const lower = wgsBox['ows:LowerCorner'].split(' ').map((s) => Number(s))
        const upper = wgsBox['ows:UpperCorner'].split(' ').map((s) => Number(s))
        const center = [
          lower[0] + (upper[0] - lower[0]) / 2,
          lower[1] + (upper[1] - lower[1]) / 2
        ]
        info.center = center
        wmstList.push({ ...info })
      })

      return wmstList
    }
  }

  getWMSParams(obj) {
    const wms = {
      url: null,
      layers: []
    }
    if (obj.WMS_Capabilities) {
      const { Capability, _attributes } = obj.WMS_Capabilities
      if (_attributes?.xmlns !== this.WMSXML) {
        return '不是标准WMS元文件地址'
      }
      wms.url =
        Capability.Request.GetFeatureInfo.DCPType.HTTP.Get.OnlineResource._attributes[
          'xlink:href'
        ]
      let Layer = Capability.Layer.Layer
      if (!Array.isArray(Layer)) {
        Layer = [Layer]
      }
      wms.layers = Layer.map((layer) => {
        const { Name, CRS, EX_GeographicBoundingBox } = layer
        const {
          eastBoundLongitude: eLon,
          northBoundLatitude: nLat,
          southBoundLatitude: sLat,
          westBoundLongitude: wLon
        } = EX_GeographicBoundingBox
        const center = [
          Number(wLon) + (Number(eLon) - Number(wLon)) / 2,
          Number(sLat) + (Number(nLat) - Number(sLat)) / 2
        ]
        return {
          center,
          name: Name,
          crs: CRS
        }
      })
    }
    return wms
  }
  getS3mParams(obj) {
    const s3m = { center: null }
    if (obj.SuperMapCache) {
      if (obj.SuperMapCache._attributes['xmlns:sml'] !== this.SUPERMAPVT) {
        return '不是标准的S3M元文件地址'
      }
      const pos = obj.SuperMapCache['sml:Position']
      s3m.center = [
        Number(pos['sml:X']),
        Number(pos['sml:Y']),
        Number(pos['sml:Z'])
      ]
    }
    return s3m
  }

  getFnByType(type) {
    if (type === 'wms') {
      return this.getWMSParams
    } else if (type === 'wmts') {
      return this.getWMTSParams
    } else if (type === 's3m') {
      return this.getS3mParams
    }
  }

  async fetchWithTimeOut(url, opt = {}) {
    try {
      const { timeout, ...options } = {
        ...opt,
        timeout: 10 * 1000
      }
      var agent = new https.Agent({
        rejectUnauthorized: false
      })
      var instance = axios.create()
      instance.defaults.httpsAgent = agent

      const response = await instance.get(url, {
        ...options
      })
      return response.data
    } catch (e) {
      console.log('error->', e)
      return 'timeout'
    }
  }

  parseXmlFromFile(str,type) {
    try {
      const xmlObj = this.parseXml2js(str)
      const info = this.getFnByType(type).bind(this)(xmlObj)
      return info
    } catch (e) {
      console.log(e)
      return null
    }
  }

  parseXmlFromUrl(url, type) {
    return new Promise((resolve, reject) => {
      if (!url) {
        reject(`URL为必填项`)
      }
      if (!['wms', 'wmts', 's3m'].includes(type)) {
        reject('type值只能为wms、wmts或者s3m')
      }
      this.fetchWithTimeOut(url)
        .then((res) => {
          if (!res) {
            return reject('can get server xml!')
          }
          try {
            // 解析xml为JS对象
            const xmlObj = this.parseXml2js(res)
            const info = this.getFnByType(type).bind(this)(xmlObj)
            if (typeof info === 'string') {
              reject(info)
            } else {
              resolve(info)
            }
          } catch (e) {
            console.log(e)
            resolve(null)
          }
        })
        .catch((e) => {
          console.log('fetchXmlFromUrl-->', e)
          reject(e)
        })
    })
  }
}

const serviceXmlParse = new ServiceXmlParse()

module.exports = { serviceXmlParse }
